package com.mapresso.gps;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 
 */

/**
 * @author adi
 * 
 */
public class Location {
	public static final double NULL_VALUE = -9999999.9999999;
	private double latitude = NULL_VALUE;
	private double longitude = NULL_VALUE;
	private static final double AverageEarthRadius = 6378135.0;
	private double elevation = NULL_VALUE;
	SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
	Date dateTime;
	double speed;
	static int nr = 0;
	private int id;
	private String dateTimeString;

	public void setTime(String timeString) {
		dateTimeString = timeString;
		try {
			if (timeString == "") {
				dateTime = sdf.parse("2000-01-01T00:00:00Z");
			} else {
				dateTime = sdf.parse(timeString);
			} 
		} catch(ParseException pe) {
				System.err.println("illegal time/date format: '"+ timeString +"'");
		}
	}

	public void setTimeDiff(long secs) {
		dateTime = new Date(dateTime.getTime() + secs);
	}

	public void setLatitude(double latitude) {
		this.latitude = latitude;
	}

	public void setLongitude(double longitude) {
		this.longitude = longitude;
	}

	public void setElevation(double elevation) {
		this.elevation = elevation;
	}

	public double getLatitude() {
		return latitude;
	}

	public double getLongitude() {
		return longitude;
	}

	public double getElevation() {
		return elevation;
	}

	public String getTimeAsString() {
		return GpsUtils.formatDate(dateTime);
	}

	public String getTimeAsStringYMD() {
		return GpsUtils.formatDateYMD(dateTime);
	}
	
	public String getTimeAsStringGPX() {
		String tmp =  GpsUtils.formatDateGPX(dateTime);
		tmp = replaceCharAt(tmp, 10, 'T');
		tmp = replaceCharAt(tmp, 19, 'Z');
		return tmp;
	}
	
	public static String replaceCharAt(String s, int pos, char c) {
		return s.substring(0,pos) + c + s.substring(pos+1);
	}
	
//	illegal time/date format: '2009-05-16 11:06:00'
//	2009-05-09T13:32:36Z

	public Date getDateTime() {
		return dateTime;
	}

	public long getTime() {
		return dateTime.getTime();
	}

	public String getTimeString() {
		return dateTimeString;
	}

	public Location(double newLatitude, double newLongitude, double elevation0, String timeString0) {
		setLatitude(newLatitude);
		setLongitude(newLongitude);
		setElevation(elevation0);
		setTime(timeString0);
		nr++;
		id = nr;
	}

	public Location(double newLatitude, double newLongitude, double elevation0) {
		this(newLatitude, newLongitude, elevation0, "");
	}

	public Location(double newLatitude, double newLongitude) {
		this(newLatitude, newLongitude, 0.0, "");
	}

	public Location() {
		this(0.0, 0.0, 0.0, "");
	}

	/**
	 * distanceFrom
	 * 
	 * @param location
	 *            Location
	 * @return Distance
	 */
	public double distanceFrom(Location location) {
		double lat1 = Math.toRadians(latitude);
		double lat2 = Math.toRadians(location.latitude);
		double lon1 = Math.toRadians(longitude);
		double lon2 = Math.toRadians(location.longitude);

		double distance = Math.acos(Math.sin(lat1) * Math.sin(lat2)
				+ Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon1 - lon2));

		return (distance * AverageEarthRadius);
	}

	public double distanceFromM(Location location) {
		double xy1[] = GpsUtils.WGS84toCH1903(latitude, longitude);
		double xy2[] = GpsUtils.WGS84toCH1903(location.latitude, location.longitude);
		double dx = xy1[0] -  xy2[0];
		double dy = xy1[1] -  xy2[1];
	    return Math.sqrt((dx * dx) + (dy * dy));

	}

	public void setSpeedFrom(Location from) {
		speed = getSpeedFrom(from);
	}

	public double getSpeed() {
		return speed;
	}

	public double getSpeedFrom(Location from) {
		double distance = distanceFrom(from);
		double time = dateTime.getTime() - from.dateTime.getTime();
		return (3600.0 * distance / time);
	}

	public boolean equals(Object obj) {
		if (!(obj instanceof Location)) {
			return false;
		} else {
			Location aLoc = (Location) obj;
			return (this.latitude == aLoc.latitude)
					&& (this.longitude == aLoc.longitude)
					&& (this.elevation == aLoc.elevation);
		}
	}

	/**
	 * Returns a hash code value for the object.
	 * 
	 * @return a hash code value for this object.
	 * @todo Implement this java.lang.Object method
	 */
	public int hashCode() {
		return new Double(this.latitude).hashCode()
				^ new Double(this.longitude).hashCode()
				^ new Double(this.elevation).hashCode();
	}

	public double getDistanceFromLocation0(Location loc) {
		double lat1 = (Math.PI / 180.0) * this.latitude;
		double lon1 = (Math.PI / 180.0) * this.longitude;
		double lat2 = (Math.PI / 180.0) * loc.getLatitude();
		double lon2 = (Math.PI / 180.0) * loc.getLongitude();
		double distance = 2 * MathUtil.asin(Math.sqrt(MathUtil.pow(Math
				.sin((lat1 - lat2) / 2), 2)
				+ Math.cos(lat1)
				* Math.cos(lat2)
				* MathUtil.pow(Math.sin((lon1 - lon2) / 2), 2)));
		return distance * 6371.0 * 1000.0;
	}
	
    public  double getDistanceFromLocation1(Location loc) {
		double lat1 = (Math.PI / 180.0) * this.latitude;
		double lon1 = (Math.PI / 180.0) * this.longitude;
		double lat2 = (Math.PI / 180.0) * loc.getLatitude();
		double lon2 = (Math.PI / 180.0) * loc.getLongitude();
        	
            double dlon = lon2 - lon1;
            double dlat = lat2 - lat1;

            double a = (Math.sin(dlat / 2) * Math.sin(dlat / 2)) + Math.cos(lat1) * Math.cos(lat2) * (Math.sin(dlon / 2) * Math.sin(dlon / 2));
            double angle = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
            return angle * 6371.0 ;
        }	
    
      
	public double getDistanceFromLocation(Location location) {
		double lat1 = Math.toRadians(latitude);
		double lat2 = Math.toRadians(location.latitude);
		double lon1 = Math.toRadians(longitude);
		double lon2 = Math.toRadians(location.longitude);

		double distance = Math.acos(Math.sin(lat1) * Math.sin(lat2)
				+ Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon1 - lon2));
		if (Double.isNaN(distance)) {
			distance = 0.0;
			System.out.println(lat1 + " " + lat2 + " " + lon1 + " " + lon2 + ": " + distance);
		}
		
//		System.out.println("%%%%" + getDistanceFromLocation0(location)  + " " +  getDistanceFromLocation1(location) + " " + (distance * AverageEarthRadius));

		return (distance * AverageEarthRadius);
	}

	/**
	 * Returns a string representation of the object.
	 * 
	 * @return a string representation of the object.
	 * @todo Implement this java.lang.Object method
	 */
	public String toString() {
		return "Location " + id + " " + dateTime + " " + GpsUtils.format(latitude, 7) 
		+ " " + GpsUtils.format(longitude, 7) + " " + elevation
		+  " " + GpsUtils.format(speed, 2);
//		return "<Location " + id +": " + latitude + ", " + longitude + ", " + elevation
//		+ ", " + dateTime + " // " + speed + " // " + dateTimeString;
	}

		

	 
}
